/**
 *  Copyright 2012 oliver.keusemann@googlemail.com
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package de.xelerity.geolessi.srv.impl.poi.bahn;

import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import de.xelerity.geolessi.preferences.Preferences;
import de.xelerity.geolessi.srv.CacheService;
import de.xelerity.geolessi.srv.InfoProvider;
import de.xelerity.geolessi.srv.InfoProvider.HALTESTELLENINFOPROVIDER;
import de.xelerity.geolessi.srv.ServiceProvider;
import de.xelerity.geolessi.srv.haltestelle.LinienProHaltestelleGetterI;
import de.xelerity.geolessi.srv.impl.poi.types.meinestadt.MSItemI;
import de.xelerity.geolessi.srv.mixed.URLGenerator;
import de.xelerity.geolessi.srv.mixed.WSCallerI;
import de.xelerity.geolessi.srv.poi.HalteStellenAdresseGetterI;
import de.xelerity.geolessi.srv.poi.HalteStellenGetterExecutorI;
import de.xelerity.geolessi.test.Test;
import de.xelerity.geolessi.types.mixed.Koordinate;
import de.xelerity.geolessi.types.poi.HalteStelle;
import de.xelerity.geolessi.types.poi.HalteStellenListe;
import de.xelerity.geolessi.types.poi.HaltestellenIDProvider;
import de.xelerity.geolessi.types.poi.NextAbfahrtInfo;
import de.xelerity.geolessi.types.verbindung.Linie;
import de.xelerity.geolessi.types.verbindung.Linien;
import de.xelerity.helper.StringFunktionen;

public class HalteSellenGetterExecutorImpl implements HalteStellenGetterExecutorI {

	private static Log log = LogFactory.getLog(HalteSellenGetterExecutorImpl.class);

	private static CacheService<Linien> cacheService = null;

	private void createHalteStelle(HalteStellenListe phalteStellenListe, MSItemI pC) {

		HalteStelle newHalteStelle = new HalteStelle();
		newHalteStelle.setTitle(pC.getTitle());
		newHalteStelle.setKoordinate(new Koordinate(pC.getLatitude(), pC.getLongitude()));
		newHalteStelle.addHaltestellenID(HaltestellenIDProvider.BAHN, pC.getId());

		HalteStellenAdresseGetterI halteStellenAdresseInfoProvider = InfoProvider
				.getHalteStellenAdresseInfoProvider(HALTESTELLENINFOPROVIDER.BAHN);

		String addressInfo = halteStellenAdresseInfoProvider.getAddressInfo(pC.getId());

		newHalteStelle.setAddressbez(addressInfo);

		newHalteStelle.addHaltestellenID(HaltestellenIDProvider.BAHN, pC.getId());

		LinienProHaltestelleGetterI linienProHaltestelleProvider = InfoProvider
				.getLinienProHaltestelleProvider(HALTESTELLENINFOPROVIDER.BAHN);

		Linien linien = null;
		try {
			int cacheKey = newHalteStelle.getIDDescAsString().hashCode();
			if (cacheService == null) {
				cacheService = new CacheService<Linien>("Linien.class");
			}
			linien = cacheService.get(cacheKey);
			if (linien == null) {
				linien = linienProHaltestelleProvider.getLinien(newHalteStelle.getIDs(HaltestellenIDProvider.BAHN));
				cacheService.put(cacheKey, linien);
			} else {
				log.info("get Linien info from Cache:" + newHalteStelle.getIDDescAsString());
			}

		} catch (Exception e) {
			log.error("'LinienProHaltestelleGetterI.getLinien' failed:" + e.getMessage(), e);
			linien = new Linien();
		}

		for (Linie L : linien) {
			long linienTyp = L.getLinienTyp();
			newHalteStelle.addHalteStelleTyp(linienTyp);
		}

		newHalteStelle.setLinien(linien);

		HalteStelle existingHalteStelle = null;
		if (Preferences.DOHSMERGE) {
			existingHalteStelle = phalteStellenListe.halteStelleExists(newHalteStelle);
		}

		// gibt es die HalteStelle schon unter anderer ID?
		if (existingHalteStelle != null) {
			log.info("HS '" + newHalteStelle.getIDDescAsString() + "' exists, merge with: '"
					+ existingHalteStelle.getIDDescAsString() + "'");
			existingHalteStelle.merge(newHalteStelle);
		} else {
			phalteStellenListe.addHalteStelle(newHalteStelle);
		}
	}

	public HalteStellenListe getHalteStellen(Koordinate pCenterSearch, long pSearchRadiusM, long pHalteStellenTypen)
			throws Exception {

		if (pHalteStellenTypen == 0 || pHalteStellenTypen < 0) {
			pHalteStellenTypen = HalteStelle.HalteStelleTyp_ALLE;
		}

		String URL = setUpUrl(pCenterSearch, pSearchRadiusM);

		WSCallerI wsCaller2 = ServiceProvider.getWSCaller();

		if (Preferences.useProxy) {
			wsCaller2.addProperty("PROXYHOST", Preferences.Proxy);
			wsCaller2.addProperty("PROXYPORT", Preferences.ProxyPort + "");
		}

		byte[] callResB = wsCaller2.call(URL);

		String callRes = new String(callResB, Preferences.ENC_ISO);

		List<HSInfo> HSInfos = parseResult(callRes, pCenterSearch);

		HalteStellenListe halteStellenListe = new HalteStellenListe();
		halteStellenListe.setCenterSearch(new Koordinate(pCenterSearch));
		halteStellenListe.setSearchRadiusM(pSearchRadiusM);

		for (HSInfo HS : HSInfos) {
			if (Long.parseLong(HS.distance) <= pSearchRadiusM) {
				log.info("Create HS:" + HS);
				createHalteStelle(halteStellenListe, HS);
			} else {
				log.info("Ignore HS:" + HS);
			}
		}

		log.info("Found '" + halteStellenListe.getHalteStellen().size() + "' Stops in radius '" + pSearchRadiusM + "' of coord: "
				+ pCenterSearch);

		if (Preferences.DOHSCLUSTER) {
			halteStellenListe.cluster();
		}
		halteStellenListe.clean();
		return halteStellenListe;
	}

	private List<HSInfo> parseResult(String S, Koordinate pCenterSearch) {
		if (StringFunktionen.isEmptyTrimed(S)) {
			return new ArrayList<HSInfo>();
		}

		// HTML-Parser http://jsoup.org/
		ArrayList<HSInfo> RES = new ArrayList<HSInfo>();
		Document doc = null;
		try {
			doc = Jsoup.parse(S);
			Elements B = doc.select("div.trow");
			for (Element src : B) {
				HSInfo lHsInfo = new HSInfo();

				Elements A = src.select("div[style]");
				for (Element Distance : A) {

					lHsInfo.distance = Distance.text();
					if (lHsInfo.distance != null) {
						lHsInfo.distance = lHsInfo.distance.replaceAll("[^0-9]", "");
					} else {
						lHsInfo.distance = "";
					}
				}

				Elements C = src.select("a[href]");

				for (Element Title : C) {

					String morInfoURL = C.attr("href");

					morInfoURL = (URLDecoder.decode(morInfoURL, Preferences.ENC_ISO));
					morInfoURL = (URLDecoder.decode(morInfoURL, Preferences.ENC_ISO));
					morInfoURL = StringEscapeUtils.unescapeHtml(URLDecoder.decode(morInfoURL, Preferences.ENC_ISO));
					String hwai = URLGenerator.getParameter(morInfoURL, "HWAI");
					hwai = hwai.replace('!', '&');
					lHsInfo.hsid = URLGenerator.getParameter(hwai, "id");
					String X = URLGenerator.getParameter(hwai, "X");
					String Y = URLGenerator.getParameter(hwai, "Y");
					lHsInfo.coord = Koordinate.fromBahnCoord(X, Y);
					if (lHsInfo.coord.isValid() && (lHsInfo.distance != null && lHsInfo.distance.length() == 0)) {
						lHsInfo.distance = lHsInfo.coord.distance(pCenterSearch) + "";
					}

					lHsInfo.title = Title.text() == null ? "" : Title.text().trim();
					break;
				}

				RES.add(lHsInfo);

			}
		} catch (Throwable e) {
			log.error("Parsing ServiceRequest failed.", e);
		}
		return RES;
	}

	private class HSInfo implements MSItemI {
		public String title = "";
		public String distance = "";
		public String hsid = "";
		public Koordinate coord;

		public String toString() {
			return title + ":" + distance + ":" + hsid + ":" + coord;
		}

		public String getId() {
			return hsid == null ? "" : hsid.trim();
		}

		public Number getLatitude() {
			return coord == null ? 0 : coord.getLatitude();
		}

		public Number getLongitude() {
			return coord == null ? 0 : coord.getLongitude();
		}

		public String getTitle() {
			return title == null ? "" : title.trim();

		}
	}

	private String setUpUrl(Koordinate pCenterSearch, long pSearchRadiusM) throws Exception {
		if (pCenterSearch == null || pCenterSearch.getLatitude() == 0 || pCenterSearch.getLongitude() == 0) {
			throw new IllegalArgumentException("pCenterSearch Koordinate nicht (korrekt) gesetzt");
		}

		URLGenerator urlGenerator = new URLGenerator("mobile.bahn.de", "bin/mobil/query2.exe/dox");

		urlGenerator.addParameter("rt", "1");
		urlGenerator.addParameter("use_realtime_filter", "1");
		urlGenerator.addParameter("performLocating", "2");
		urlGenerator.addParameter("tpl", "stopsnear");
		urlGenerator.addParameter("look_maxdist", pSearchRadiusM + "");

		urlGenerator.addParameter("look_x", Koordinate.toBahnCoord(pCenterSearch.getLongitude()));
		urlGenerator.addParameter("look_y", Koordinate.toBahnCoord(pCenterSearch.getLatitude()));

		return urlGenerator.getURL();
	}

	public static void main(String[] args) {

		Test.startTest();

		String CALL = "Request:http://geolessiprovider.appspot.com/geths?x=9.261328&y=48.726194&spanx=0.005148&spany=0.005392";

		boolean SHOWABFAHRTEN = false;

		HashMap<String, Double> paras = getParas(CALL);

		Koordinate pCenterSearch = new Koordinate(paras.get("y"), paras.get("x"));

		try {

			double r = 0;
			if (paras.get("spanx") >= paras.get("spany")) {
				r = pCenterSearch.distance(pCenterSearch.getLatitude(), pCenterSearch.getLongitude() + paras.get("spanx")) / 2.0;
			} else {
				r = pCenterSearch.distance(pCenterSearch.getLatitude() + paras.get("spany"), pCenterSearch.getLongitude()) / 2.0;
			}
			HalteStellenListe halteStellen = new HalteSellenGetterFacadeImpl().getHalteStellen(pCenterSearch, paras.get("spanx"),
					paras.get("spany"), 0L);

			System.out.println("\nRadius:" + r);

			halteStellen.print();

			if (SHOWABFAHRTEN) {

				for (HalteStelle hs : halteStellen.getHalteStellen()) {
					List<String> iDs = hs.getIDs(HaltestellenIDProvider.BAHN);
					hs.getHalteStelleTyp();

					List<NextAbfahrtInfo> res = new HalteStelleNextAbfahrtGetterImpl().getNextAbfahrten(iDs, hs.getHalteStelleTyp(),
							(new java.util.Date().getTime() + Preferences.ServerTimeOffset));

					String HSID = "";
					for (String S : iDs) {
						HSID = HSID + S + ", ";
					}

					System.out.println("------------------------------------------------------------------------- "
							+ HSID.replaceAll(",\\s*$", "") + " ---");
					for (NextAbfahrtInfo nabi : res) {
						System.out.println(nabi.toString());
					}

				}
			}
		} catch (Throwable e) {
			e.printStackTrace();
		} finally {
			Test.endTest();
		}
	}

	private static HashMap<String, Double> getParas(String S) {
		S = S.toLowerCase().replaceAll("\\s", "");
		HashMap<String, Double> ret = new HashMap<String, Double>();
		String[] split = S.split("(\\?|&)");
		for (String p : split) {
			String[] split2 = p.split("=");
			if (split2.length == 2)
				ret.put(split2[0], Double.parseDouble(split2[1]));
		}
		return ret;
	}
}
